#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/times.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <sched.h>
#include <linux/sched.h>

int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...);

#define _GNU_SOURCE
int licznik = 0;

int inc(void * stack) {
	licznik++;
	return 0;
}

int main(int argc, char ** argv) {
	if (argc != 2) {
		printf("Zła liczba argumentów. Poprawne wywołanie: ./zad1 <liczba>\n");
		return -1;
	}
	int N = atoi(argv[1]);
	if (N < 1) {
		printf("Argument musi być liczbą całkowitą większą od 0\n");
	}
	printf("%d\n", N);
	int i, status;
	pid_t pid;
	struct czas{
		struct timeval sys;
		struct timeval usr;
		struct timeval real;
	} children = {{0, 0}, {0, 0}, {0, 0}}, parent = {{0, 0}, {0, 0}, {0, 0}};
	struct timeval tmp1 = {0, 0}, tmp2 = {0, 0};
	gettimeofday(&parent.real, NULL);
	#if defined(CLONE) || defined(CLONEV)
	void * stack = malloc(512);
	stack += 512;
	#endif
	for (i = 0; i < N; i++) {
		#ifdef FORK
		pid = fork();
		#elif VFORK
		pid = vfork();
		#endif
		#if defined(FORK) || defined(VFORK)
		gettimeofday(&tmp1, NULL);
		if (pid < 0) {
			perror("(V)FORK error");
			exit(1);
		} else if (pid == 0) {
			licznik++;
			exit(0);
		} else {
			waitpid(pid, &status, 0);
		}
		gettimeofday(&tmp2, NULL);
		#endif
		#if defined(CLONE) || defined(CLONEV)
		gettimeofday(&tmp1, NULL);
		#ifdef CLONE
		pid = clone(inc, stack, SIGCHLD | CLONE_FS, NULL);
		#elif CLONEV
		pid = clone(inc, stack, SIGCHLD | CLONE_FS | CLONE_VM | CLONE_VFORK | CLONE_FS | CLONE_FILES, NULL);
		#endif
		gettimeofday(&tmp2, NULL);
		waitpid(pid, &status, 0);
		#endif
		children.real.tv_sec += (tmp2.tv_sec - tmp1.tv_sec);
		children.real.tv_usec += (tmp2.tv_usec - tmp1.tv_usec);
	}
	printf("Licznik = %d\n", licznik);
	gettimeofday(&tmp1, NULL);
	parent.real.tv_sec = tmp1.tv_sec - parent.real.tv_sec;
	parent.real.tv_usec = tmp1.tv_usec - parent.real.tv_usec;
	struct rusage usage;
	getrusage(RUSAGE_SELF, &usage);
	parent.sys.tv_sec = usage.ru_stime.tv_sec;
	parent.sys.tv_usec = usage.ru_stime.tv_usec;
	parent.usr.tv_sec = usage.ru_utime.tv_sec;
	parent.usr.tv_usec = usage.ru_utime.tv_usec;
	getrusage(RUSAGE_CHILDREN, &usage);
	children.sys.tv_sec = usage.ru_stime.tv_sec;
	children.sys.tv_usec = usage.ru_stime.tv_usec;
	children.usr.tv_sec = usage.ru_utime.tv_sec;
	children.usr.tv_usec = usage.ru_utime.tv_usec;
	printf("Parent sys time: %f\n", (float)(parent.sys.tv_sec + parent.sys.tv_usec/1000000.0));
	printf("Parent usr time: %f\n", (float)(parent.usr.tv_sec + parent.usr.tv_usec/1000000.0));
	printf("Parent real time: %f\n", (float)(parent.real.tv_sec + parent.real.tv_usec/1000000.0));
	printf("Children sys time: %f\n", (float)(children.sys.tv_sec + children.sys.tv_usec/1000000.0));
	printf("Children usr time: %f\n", (float)(children.usr.tv_sec + children.usr.tv_usec/1000000.0));
	printf("Children real time: %f\n", (float)(children.real.tv_sec + children.real.tv_usec/1000000.0));
	return 0;
}
